/* === LICENSE INFO ===
 * This source file is subject to the GPLv3 license that is bundled with this
 * package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl.txt
 * === end of LICENSE INFO === */

/*==============================================================================
  File Name   : LogConfig.cpp
  Description : Definition for LogConfig class in GLog module.

  Created on  : 2010-11-27
  Updated on  : 2011-01-30
  Updated by  : Lee Shiou Ming

  Cautions    : (important notes)
  ============================================================================*/

//------------------------------------------------------------------------------
//-- INCLUDES & DEFINES
//------------------------------------------------------------------------------
#include "LogConfig.hpp"

#include "GDateTime/GDateTime.hpp"
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
#include <sstream>
#include <string>

using namespace gUtil::GLog;


//------------------------------------------------------------------------------
//-- INITIALIZATIONS
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
//-- FUNCTION IMPLEMENTATIONS
//------------------------------------------------------------------------------
LogConfig::LogConfig()
:
    m_isConfigured(false),
    m_maxLogLevel(DEFAULT_MAX_LOG_LEVEL),
    m_logFilePath(DEFAULT_LOG_FILE_PATH),
    m_logRotationSize(DEFAULT_LOG_ROTATION_SIZE_BYTE),
    m_logRotationInterval(DEFAULT_LOG_ROTATION_INTERVAL)
{
}


bool LogConfig::loadFromFile(const std::string& _configFile,
        const ConfigFileFormat _configFileFormat)
{
    if (CONFIG_FILE_UNSUPPORTED == _configFileFormat)
    {
        std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                ": Unsupported config file format." << std::endl;
        return false;
    }

    boost::property_tree::ptree logCfgProperties;

    try
    {
        std::cout << "Reading GLog config file: " << _configFile << std::endl;
        read_xml(_configFile, logCfgProperties);

        int enumValue = -1;

        //=== Reading max log level
        std::string logLevelCfg =
                logCfgProperties.get<std::string>(LOG_CFG_LOG_LEVEL);
        std::cout << "- max log level [" << logLevelCfg << "]" << std::endl;

        // Transform log level cfg value to upper case. If user specifies value
        // corresponding to log level string, we can support that too.
        std::transform(logLevelCfg.begin(), logLevelCfg.end(),
                logLevelCfg.begin(), ::toupper);

        enumValue = getEnumValue(logLevelCfg, LOG_LEVEL_VALUE_MAP_TABLE);
        if (enumValue >= 0)
        {
            m_maxLogLevel = static_cast<LogLevel>(enumValue);
        }
        else
        {
            std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                    ": Invalid log level." << std::endl;
            return false;
        }
        //=== end of reading max log level

        //=== Reading enabled log output.
        BOOST_FOREACH(boost::property_tree::ptree::value_type& enabledOutput,
                logCfgProperties.get_child(LOG_CFG_ENABLED_OUTPUT))
        {
            std::string logOutputTypeCfg = enabledOutput.second.get<std::string>("");

            std::cout << "- enabled log output [" << logOutputTypeCfg << "]" <<
                    std::endl;

            std::transform(logOutputTypeCfg.begin(), logOutputTypeCfg.end(),
                    logOutputTypeCfg.begin(), ::toupper);

            enumValue = getEnumValue(logOutputTypeCfg,
                    LOG_OUTPUT_TYPE_VALUE_MAP_TABLE);
            if (enumValue >= 0)
            {
                m_enabledOutputTypes.insert(static_cast<LogOutputType>(enumValue));
            }
            else
            {
                std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                        ": Invalid log output type." << std::endl;
                return false;
            }
        }
        //=== end of reading enabled log output.

        // Remaining settings are related to log file. Skip them if FILE output
        // is not enabled.
        if (m_enabledOutputTypes.find(OUTPUT_FILE) == m_enabledOutputTypes.end())
        {
            return true;
        }

        //=== Reading log file path
        std::string logFilePathCfg =
                logCfgProperties.get<std::string>(LOG_CFG_LOG_FILE_PATH);
        std::cout << "- log file path [" << logFilePathCfg << "]" << std::endl;

        if (logFilePathCfg.empty())
        {
            std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                    ": Log file path is missing." << std::endl;
            return false;
        }
        m_logFilePath = logFilePathCfg;
        //=== end of reading log file path

        //=== Reading log file rotation size
        std::string logRotationSizeCfg =
                logCfgProperties.get<std::string>(LOG_CFG_ROTATION_BY_SIZE_KB);
        std::cout << "- log rotation size [" << logRotationSizeCfg << " KB]" <<
                std::endl;

        if (logRotationSizeCfg.empty())
        {
            std::cout << "Log rotation size not configured. " <<
                    "Leave it as default size [" << m_logRotationSize <<
                    " Bytes]." << std::endl;
        }
        else
        {
            std::istringstream inputStream(logRotationSizeCfg);
            unsigned int tmpRotationSize = 0;
            if (inputStream >> tmpRotationSize)
            {
                // Input is in KB hence times 1000.
                if (tmpRotationSize * 1000 < 1)
                {
                    std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                            ": Configured rotation size [" << tmpRotationSize <<
                            " KB] is too small. Leave it as default size [" <<
                            m_logRotationSize << " Bytes]." << std::endl;
                }
                else if (tmpRotationSize * 1000 > MAX_LOG_FILE_SIZE_BYTE)
                {
                    m_logRotationSize = MAX_LOG_FILE_SIZE_BYTE;
                    std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                            ": Configured rotation size [" << tmpRotationSize <<
                            " KB] is greater than MAX_LOG_FILE_SIZE_BYTE. " <<
                            "Set it to max [" << m_logRotationSize <<
                            " Bytes]." << std::endl;
                }
                else
                {
                    m_logRotationSize = tmpRotationSize * 1000;
                }
            }
            else
            {
                std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                        ": Invalid log rotation size. Disabled!" << std::endl;
            }
        }
        //=== end of reading log file rotation size

        //=== Reading log file rotation interval
        std::string logRotationIntervalCfg =
                logCfgProperties.get<std::string>(LOG_CFG_ROTATION_BY_INTERVAL);
        std::cout << "- log rotation interval [" <<
                logRotationIntervalCfg << "]" << std::endl;

        enumValue = getEnumValue(logRotationIntervalCfg,
                TIME_INTERVAL_VALUE_MAP_TABLE);
        if (enumValue >= 0)
        {
            m_logRotationInterval = static_cast<TimeInterval>(enumValue);
        }
        else
        {
            m_logRotationInterval = TIME_INTERVAL_NONE;
            std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ <<
                    ": Invalid log rotation interval. Disabled!" << std::endl;
        }
        //=== end of reading log file rotation interval

    }
    catch (const std::exception& e)
    {
        std::cerr << "[ERROR] " << __PRETTY_FUNCTION__ << ": " << e.what();
        std::cerr << std::endl;
        return false;
    }

    return true;
}
